﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


namespace Common.JWT
{
    public class JsonToken : IJsonToken
    {
        private IJsonSerializer JsonSerializer { get; set; }

        private readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);

        private readonly IDictionary<JwtHashAlgorithm, Func<byte[], byte[], byte[]>> HashAlgorithms;

        public JsonToken()
        {
            JsonSerializer = new DefaultJsonSerializer();
            HashAlgorithms = JwtHelper.GetHashAlgorithms();
        }

        public JsonToken(IJsonSerializer jsonSerializer)
        {
            JsonSerializer = jsonSerializer;
            HashAlgorithms = JwtHelper.GetHashAlgorithms();
        }

        #region 公共方法

        public T Decode<T>(string token, string key)
        {
            if (string.IsNullOrEmpty(token))
            {
                return default(T);
            }
            var parts = token.Split('.');
            if (parts.Length != 2)
            {
                throw new ArgumentException("Token格式不对");
            }
            var decodedCrypto = parts[1];
            var result = Verify(parts[0], decodedCrypto, key);
            if (!string.IsNullOrEmpty(result))
            {
                return JsonSerializer.Deserialize<T>(result);
            }
            return default(T);
        }

        public string Encode<T>(T payload, string key, JwtHashAlgorithm algorithm = JwtHashAlgorithm.HS256)
        {
            return Encode(new Dictionary<string, string>(), payload, Encoding.UTF8.GetBytes(key), algorithm);
        }

        public string Encode<T>(IDictionary<string, string> extraHeaders, T payload, string key, JwtHashAlgorithm algorithm = JwtHashAlgorithm.HS256)
        {
            return Encode(extraHeaders,payload, Encoding.UTF8.GetBytes(key), algorithm);
        }

        public bool Verify(string payload, string payloadJson, string header, string decodedCrypto, string key)
        {
            var str = Verify(header, decodedCrypto, key);
            return string.IsNullOrEmpty(str) ? false : true;
        }

        public bool IsDataExp(string header)
        {
            var headerJson = Encoding.UTF8.GetString(JwtHelper.Base64UrlDecode(header));
            var headerData = JsonSerializer.Deserialize<Dictionary<string, string>>(headerJson);
            string expObj = string.Empty;
            //exp是数据的过期时间
            if (!headerData.TryGetValue("exp", out expObj) || string.IsNullOrEmpty(expObj))
            {
                return false;
            }
            int expInt;
            try
            {
                expInt = Convert.ToInt32(expObj);
            }
            catch (FormatException)
            {
                throw new Exception("Claim 'exp' 必须是正整数.");
            }
            var secondsSinceEpoch = Math.Round((DateTime.UtcNow - UnixEpoch).TotalSeconds);
            if (secondsSinceEpoch >= expInt)
            {
                return false;
            }
            return true;
        }
        #endregion

        #region 私有方法

        private string Verify(string header, string crypto, string key)
        {
            var headerJson = Encoding.UTF8.GetString(JwtHelper.Base64UrlDecode(header));
            var headerData = JsonSerializer.Deserialize<Dictionary<string, string>>(headerJson);
            var algorithm = (string)headerData["alg"];
            using (var alg = JwtHelper.GetVerityDataHash()(algorithm, Encoding.UTF8.GetBytes(key)))
            {
                byte[] data = JwtHelper.Base64UrlDecode(crypto);
                var receivedHash = data.Take(alg.HashSize >> 3);
                //提取数据本身
                var dataContent = data.Skip(alg.HashSize >> 3).ToArray();
                //计算数据哈希值和收到的哈希值
                var computedHash = alg.ComputeHash(dataContent);
                //如果相等则数据正确
                if (receivedHash.SequenceEqual(computedHash))
                {
                    return Encoding.UTF8.GetString(dataContent);
                }
                return string.Empty;
            }
        }
        private string Encode<T>(IDictionary<string, string> extraHeaders, T payload, byte[] key, JwtHashAlgorithm algorithm = JwtHashAlgorithm.HS256)
        {
            var segments = new List<string>();
            var header = new Dictionary<string, string>(extraHeaders)
            {
                { "typ", "JWT-C" },
                { "alg", algorithm.ToString() },
            };
            var headerBytes = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(header));
            var payloadBytes = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(payload));
            segments.Add(JwtHelper.Base64UrlEncode(headerBytes));
            var signature = HashAlgorithms[algorithm](key, payloadBytes);
            segments.Add(JwtHelper.Base64UrlEncode(signature.Concat(payloadBytes).ToArray()));
            return string.Join(".", segments.ToArray());
        }
        #endregion
    }
}
